Introduction to Flutter Animations
Animations are an important part of modern Flutter application development. They help make applications feel responsive, interactive, and visually engaging. Flutter provides animation APIs that can be used for simple property changes as well as highly customized motion effects.
Flutter supports both implicit and explicit animations. Implicit animations are easier to implement because Flutter manages the intermediate animation values for you, while explicit animations provide more control using classes such as AnimationController, Tween, and CurvedAnimation.
1. What Are Animations?
An animation is a visual change that happens over a period of time instead of changing instantly.
For example, instead of immediately changing a container from 100 pixels to 300 pixels, an animation can gradually change its size over 500 milliseconds.
Without Animation:
Old Size → Instant Change → New Size
With Animation:
Old Size → Small Change → Medium Change → Large Change → New Size
Animations can be used for movement, resizing, fading, rotation, color changes, page transitions, list changes, and many other UI effects.
2. Why Are Animations Important in Flutter?
Well-designed animations communicate changes in the interface and provide visual feedback to users.
Common Benefits
- Make interfaces feel more responsive
- Provide feedback after user interactions
- Make transitions between states easier to understand
- Improve visual continuity
- Highlight important UI changes
- Create smoother navigation experiences
- Improve the overall visual quality of an application
Examples
- Button press animation
- Loading animation
- Page transition
- Animated menu
- Expanding card
- Image zoom
- Fade-in content
- Sliding drawer
- Animated list item
3. Flutter Animation System
Flutter's animation system is based on typed Animation objects. An animation represents a value that can change over time, and widgets can use that changing value to update their appearance.
Main building blocks:
Animation
AnimationController
Tween
Animatable
CurvedAnimation
AnimatedWidget
AnimatedBuilder
- Implicitly animated widgets
- Transition widgets
4. Basic Animation Flow
User Interaction
↓
State Changes
↓
Animation Starts
↓
Animation Value Changes
↓
Widget Rebuilds
↓
Visual Change
For explicit animations, an AnimationController typically generates values over time. A Tween can then map those values to the required property range.
5. Types of Flutter Animations
| Type | Description | Typical Use |
| Implicit Animation | Flutter automatically manages the animation between old and new property values | Size, color, padding, opacity |
| Explicit Animation | Developer controls the animation using an animation controller | Custom and complex animations |
| Transition Animation | Uses animation values to create specific visual transitions | Fade, slide, scale, rotation |
| Hero Animation | Animates a widget between two routes | Image or card transitions |
| Staggered Animation | Combines multiple animations with different timing intervals | Complex entrance animations |
| Physics-Based Animation | Uses simulations such as springs or other physical behavior | Natural movement |
6. Implicit Animations
Implicit animations are the simplest way to add animation to many Flutter widgets. You change a widget's property and Flutter automatically animates the transition between the previous and new value.
Flutter provides a collection of implicitly animated widgets, including AnimatedContainer, AnimatedOpacity, AnimatedPadding, and AnimatedPositioned.
Basic Example
AnimatedContainer(
duration: const Duration(seconds: 1),
width: isExpanded ? 300 : 100,
height: 100,
color: isExpanded ? Colors.blue : Colors.red,
child: const Center(
child: Text('Animated'),
),
)
How It Works
Old Property
↓
New Property
↓
Flutter Calculates Intermediate Values
↓
Animated Result
When to Use Implicit Animations
- Simple property changes
- Small UI animations
- Color, size, padding, opacity, or position changes
7. AnimatedContainer
AnimatedContainer automatically animates changes to many container properties.
class AnimatedBox extends StatefulWidget {
const AnimatedBox({super.key});
@override
State createState() => _AnimatedBoxState();
}
class _AnimatedBoxState extends State {
bool expanded = false;
@override
Widget build(BuildContext context) {
return Column(
children: [
AnimatedContainer(
duration: const Duration(milliseconds: 500),
width: expanded ? 300 : 100,
height: expanded ? 200 : 100,
decoration: BoxDecoration(
color: expanded ? Colors.blue : Colors.orange,
borderRadius: BorderRadius.circular(
expanded ? 30 : 10,
),
),
),
ElevatedButton(
onPressed: () {
setState(() {
expanded = !expanded;
});
},
child: const Text('Animate'),
),
],
);
}
}
Important Properties
duration
curve
width / height
color
padding / margin
decoration
alignment
8. Animation Duration
The duration property determines how long an animation takes to move from its old value to its new value.
AnimatedContainer(
duration: const Duration(milliseconds: 500),
width: 250,
height: 150,
)
| Duration | Typical Feel |
| 100ms | Very quick |
| 200ms | Quick |
| 300ms | Natural for many UI interactions |
| 500ms | Clearly visible transition |
| 1000ms | Slow animation |
9. Animation Curves
A Curve changes the timing behavior of an animation. Flutter provides many built-in curves:
Curves.linear
Curves.easeIn
Curves.easeOut
Curves.easeInOut
Curves.bounceIn
Curves.bounceOut
Curves.elasticIn
Curves.elasticOut
AnimatedContainer(
duration: const Duration(milliseconds: 600),
curve: Curves.easeInOut,
width: 300,
height: 150,
)
10. AnimatedOpacity
AnimatedOpacity is useful for smoothly changing the visibility of a widget by animating its opacity.
AnimatedOpacity(
opacity: isVisible ? 1.0 : 0.0,
duration: const Duration(milliseconds: 500),
child: const Text('Hello Flutter'),
)
Useful For
- Fade-in and fade-out effects
- Showing and hiding content
- Loading and success messages
11. AnimatedPadding
AnimatedPadding animates changes to the padding around a child.
AnimatedPadding(
duration: const Duration(milliseconds: 400),
padding: EdgeInsets.all(isLarge ? 40 : 10),
child: const Text('Animated Padding'),
)
Useful for expanding cards, responsive interactions, and animated layouts.
12. AnimatedPositioned
AnimatedPositioned animates changes to the position of a child inside a Stack.
Stack(
children: [
AnimatedPositioned(
duration: const Duration(milliseconds: 500),
left: isMoved ? 200 : 20,
top: 50,
child: const FlutterLogo(size: 60),
),
],
)
Useful For
- Moving cards
- Sliding buttons
- Animated overlays
- Interactive layouts
13. AnimatedSwitcher
AnimatedSwitcher animates the transition when its child changes.
AnimatedSwitcher(
duration: const Duration(milliseconds: 400),
child: Text(
'$count',
key: ValueKey(count),
),
)
Note: The key helps Flutter recognize that the displayed child has changed.
Useful For
- Changing text or icons
- Success / error messages
- Loading indicators
- Switching between widgets
14. TweenAnimationBuilder
TweenAnimationBuilder is useful when you want a custom implicit animation without manually creating an AnimationController.
TweenAnimationBuilder(
tween: Tween(begin: 0, end: 200),
duration: const Duration(seconds: 1),
builder: (context, value, child) {
return Container(
width: value,
height: value,
color: Colors.blue,
);
},
)
15. Explicit Animations
Explicit animations provide more control over the animation lifecycle. Instead of simply changing a property, the developer controls the animation using an AnimationController.
AnimationController
↓
Animation / Tween
↓
Curve
↓
Animated Widget
↓
UI
16. AnimationController
AnimationController is used to control an explicit animation.
late AnimationController controller;
@override
void initState() {
super.initState();
controller = AnimationController(
duration: const Duration(seconds: 1),
vsync: this,
);
}
Common Methods
forward()
reverse()
repeat()
stop()
reset()
animateTo()
fling()
animateWith()
17. Understanding vsync
The vsync parameter connects an animation controller to the screen's frame scheduling system.
class MyAnimation extends StatefulWidget {
const MyAnimation({super.key});
@override
State createState() => _MyAnimationState();
}
class _MyAnimationState extends State
with SingleTickerProviderStateMixin {
late AnimationController controller;
@override
void initState() {
super.initState();
controller = AnimationController(
duration: const Duration(seconds: 1),
vsync: this,
);
}
@override
void dispose() {
controller.dispose();
super.dispose();
}
}
SingleTickerProviderStateMixin is commonly used when a state object manages a single animation controller.
18. Why Dispose AnimationController?
An AnimationController should be disposed when the widget is removed from the widget tree to release resources.
@override
void dispose() {
controller.dispose();
super.dispose();
}
19. Animation Values
An Animation commonly produces values between 0.0 and 1.0:
0.0 → 0.25 → 0.50 → 0.75 → 1.0
The current value determines how a widget should appear at that point in the animation.
20. Tween
A Tween defines a beginning value and an ending value and interpolates between them.
final Tween sizeTween = Tween(
begin: 50,
end: 200,
);
A controller supplies a normalized value, while the tween maps that value to the desired output range.
21. Different Types of Tweens
Tween
ColorTween
IntTween
RectTween
SizeTween
AlignmentTween
EdgeInsetsTween
ColorTween Example
final animation = ColorTween(
begin: Colors.red,
end: Colors.blue,
).animate(controller);
IntTween Example
final animation = IntTween(
begin: 0,
end: 100,
).animate(controller);
22. CurvedAnimation
CurvedAnimation changes the timing behavior of an animation by applying a curve to the parent animation.
final animation = CurvedAnimation(
parent: controller,
curve: Curves.easeInOut,
);
23. Connecting Tween and AnimationController
final animation = Tween(
begin: 50,
end: 200,
).animate(controller);
Complete Relationship
AnimationController
↓
0.0 → 1.0
↓
Tween
↓
50 → 200
↓
Widget Size
24. AnimatedBuilder
AnimatedBuilder is useful when an animation needs to be integrated into a larger widget's build method.
AnimatedBuilder(
animation: controller,
builder: (context, child) {
return Transform.scale(
scale: controller.value,
child: child,
);
},
child: const FlutterLogo(),
)
AnimatedBuilder listens to the animation and rebuilds only the part of the widget tree represented by its builder.
25. AnimatedWidget
AnimatedWidget is useful when creating a reusable widget whose appearance depends on an animation.
Animation
↓
AnimatedWidget
↓
Build UI
Tip: Use AnimatedWidget for reusable animated widgets and AnimatedBuilder for integrating animations into larger build methods.
26. Transition Widgets
Flutter provides several built-in transition widgets that make common explicit animations easier to implement.
FadeTransition
ScaleTransition
SlideTransition
RotationTransition
SizeTransition
PositionedTransition
AlignTransition
27. FadeTransition
FadeTransition(
opacity: animation,
child: const FlutterLogo(),
)
Useful for fade-in and fade-out effects with explicit animation control.
28. ScaleTransition
ScaleTransition(
scale: animation,
child: const FlutterLogo(),
)
Used for zooming or scaling effects.
29. SlideTransition
SlideTransition(
position: animation,
child: const Text('Slide Me'),
)
Useful for moving a widget from one position to another.
30. RotationTransition
RotationTransition(
turns: animation,
child: const Icon(Icons.refresh),
)
Used for rotating icons, images, or other widgets.
31. Hero Animations
A Hero animation creates a shared-element transition between two routes. A widget appears to move from its position on one screen to a corresponding position on another screen.
Source Route
Hero(
tag: 'product-image',
child: Image.network('https://example.com/product.jpg'),
)
Destination Route
Hero(
tag: 'product-image',
child: Image.network('https://example.com/product.jpg'),
)
Matching Hero tags on source and destination routes allow the framework to animate the shared element between routes.
Useful For
- Product images
- Profile pictures
- Photo galleries
- Cards opening into detail pages
- Shared element navigation
32. Staggered Animations
A staggered animation divides a larger animation into multiple smaller animations with different timing intervals.
Animation
│
├── Logo 0% → 30%
├── Title 20% → 50%
├── Image 40% → 70%
└── Button 60% → 100%
This creates a sequence where elements appear one after another or partially overlap in time.
33. Animation Status
An animation has a status that describes its current lifecycle.
Common AnimationStatus Values
dismissed
forward
reverse
completed
Listening to Animation Status
controller.addStatusListener((status) {
if (status == AnimationStatus.completed) {
print('Animation completed');
}
});
34. Animation Listener
An animation can notify listeners whenever its value changes.
controller.addListener(() {
setState(() {});
});
This pattern is useful for simple custom animations, although AnimatedBuilder can often handle rebuilding automatically.
35. Forward and Reverse Animation
Forward
controller.forward();
Moves the animation from its current value toward the upper bound.
Reverse
controller.reverse();
Moves the animation toward its lower bound.
Toggle Example
if (controller.status == AnimationStatus.completed) {
controller.reverse();
} else {
controller.forward();
}
36. Repeating Animations
controller.repeat();
Useful For
- Loading indicators
- Rotating icons
- Background effects
- Continuous visual effects
Remember: Stop or dispose controllers appropriately when the widget no longer needs the animation.
37. Physics-Based Animations
Flutter supports animations driven by physical simulations, creating motion that feels more natural than simple linear interpolation.
Examples:
- Spring movement
- Bounce effects
- Fling gestures
- Natural scrolling-like motion
AnimationController supports methods such as fling() and animateWith() for simulation-driven animation.
38. Choosing Between Implicit and Explicit Animations
| Requirement | Recommended Starting Point |
| Simple size change | AnimatedContainer |
| Simple opacity change | AnimatedOpacity |
| Simple padding change | AnimatedPadding |
| Simple child replacement | AnimatedSwitcher |
| Custom property interpolation | TweenAnimationBuilder |
| Full animation control | AnimationController |
| Fade with explicit control | FadeTransition |
| Slide with explicit control | SlideTransition |
| Complex reusable animation | AnimatedWidget or AnimatedBuilder |
| Animation between screens | Hero or route transition |
39. Implicit vs Explicit Animations
| Feature | Implicit | Explicit |
| Ease of Use | Easy | More advanced |
| Control | Limited | High |
| Controller Required | Usually no | Usually yes |
| Basic Property Changes | Excellent | Possible but often unnecessary |
| Complex Timing | Limited | Excellent |
| Repeated Animation | Not the primary use case | Supported |
| Custom Sequences | Limited | Excellent |
| Learning Difficulty | Low | Medium to High |
40. Animation Example: Expanding Card
class ExpandableCard extends StatefulWidget {
const ExpandableCard({super.key});
@override
State createState() => _ExpandableCardState();
}
class _ExpandableCardState extends State {
bool expanded = false;
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
setState(() {
expanded = !expanded;
});
},
child: AnimatedContainer(
duration: const Duration(milliseconds: 400),
width: double.infinity,
height: expanded ? 250 : 120,
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.blue,
borderRadius: BorderRadius.circular(expanded ? 24 : 12),
),
child: Column(
children: [
const Text(
'Product Card',
style: TextStyle(color: Colors.white, fontSize: 20),
),
if (expanded)
const Text(
'Additional product information',
style: TextStyle(color: Colors.white),
),
],
),
),
);
}
}
41. Animation Example: Fade-In Widget
class FadeExample extends StatefulWidget {
const FadeExample({super.key});
@override
State createState() => _FadeExampleState();
}
class _FadeExampleState extends State {
bool visible = false;
@override
Widget build(BuildContext context) {
return Column(
children: [
AnimatedOpacity(
opacity: visible ? 1 : 0,
duration: const Duration(milliseconds: 600),
child: const FlutterLogo(size: 100),
),
ElevatedButton(
onPressed: () {
setState(() {
visible = !visible;
});
},
child: const Text('Toggle'),
),
],
);
}
}
42. Animation Example: Explicit Scale Animation
class ScaleExample extends StatefulWidget {
const ScaleExample({super.key});
@override
State createState() => _ScaleExampleState();
}
class _ScaleExampleState extends State
with SingleTickerProviderStateMixin {
late AnimationController controller;
late Animation scale;
@override
void initState() {
super.initState();
controller = AnimationController(
duration: const Duration(milliseconds: 800),
vsync: this,
);
scale = Tween(begin: 0.5, end: 1.0).animate(
CurvedAnimation(parent: controller, curve: Curves.easeOut),
);
controller.forward();
}
@override
void dispose() {
controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return ScaleTransition(
scale: scale,
child: const FlutterLogo(size: 120),
);
}
}
43. Animation Example: Rotating Icon
class RotateExample extends StatefulWidget {
const RotateExample({super.key});
@override
State createState() => _RotateExampleState();
}
class _RotateExampleState extends State
with SingleTickerProviderStateMixin {
late AnimationController controller;
@override
void initState() {
super.initState();
controller = AnimationController(
duration: const Duration(seconds: 2),
vsync: this,
)..repeat();
}
@override
void dispose() {
controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return RotationTransition(
turns: controller,
child: const Icon(Icons.refresh, size: 60),
);
}
}
44. Animation Example: Slide Transition
class SlideExample extends StatefulWidget {
const SlideExample({super.key});
@override
State createState() => _SlideExampleState();
}
class _SlideExampleState extends State
with SingleTickerProviderStateMixin {
late AnimationController controller;
late Animation position;
@override
void initState() {
super.initState();
controller = AnimationController(
duration: const Duration(milliseconds: 700),
vsync: this,
);
position = Tween(
begin: const Offset(-1, 0),
end: Offset.zero,
).animate(
CurvedAnimation(parent: controller, curve: Curves.easeOut),
);
controller.forward();
}
@override
void dispose() {
controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return SlideTransition(
position: position,
child: const Text(
'Welcome to Flutter',
style: TextStyle(fontSize: 24),
),
);
}
}
45. Animation and User Interaction
Animations often respond to user actions.
Common Triggers
- Button tap
- Gesture / swipe / drag
- Scroll
- Navigation
- State change
- API response
onPressed: () {
controller.forward();
}
onPressed: () {
controller.reverse();
}
46. Animations and State Management
Animations are often connected to application state.
bool isExpanded = false;
setState(() {
isExpanded = !isExpanded;
});
AnimatedContainer(
duration: const Duration(milliseconds: 400),
height: isExpanded ? 300 : 100,
)
For more advanced control, an AnimationController can manage the animation independently from a boolean state variable.
47. Animations and Navigation
Animations can improve transitions between application screens.
Common Navigation Animation Patterns
- Fade transition
- Slide transition
- Scale transition
- Hero transition
- Shared element transition
48. Animating Lists
AnimatedList can be used to animate changes when list items are inserted or removed.
List Item Added
↓
Animation Starts
↓
Item Appears
↓
List Updates
49. Animation Performance
Animations run continuously across frames, so performance should be considered when designing complex effects.
Good Practices
- Keep animations simple when possible
- Avoid unnecessary widget rebuilds
- Use
AnimatedBuilder to isolate rebuilding
- Dispose animation controllers
- Avoid excessive simultaneous animations
- Test animations on real devices
- Use appropriate animation durations
50. Avoid Overusing Animations
Animations should support the user experience rather than distract from it.
Avoid:
- Animating every widget unnecessarily
- Very long transitions for simple actions
- Constant motion that distracts users
- Animations that make navigation feel slow
- Complex effects when a simple fade or slide is sufficient
51. Accessibility Considerations
Some users may prefer reduced motion or may be sensitive to excessive animation.
Good Practices
- Do not rely only on motion to communicate meaning
- Keep important information accessible without animation
- Avoid excessive flashing or rapid movement
- Use animation to support usability rather than distract from content
52. Common Animation Mistakes
Mistake 1: Forgetting to Dispose the Controller
@override
void dispose() {
controller.dispose();
super.dispose();
}
Mistake 2: Using Explicit Animation for a Simple Property Change
If AnimatedContainer can solve the problem, creating a complete animation controller may be unnecessary.
Mistake 3: Using Very Long Durations
Animations that take too long can make the application feel slow.
Mistake 4: Rebuilding Too Much UI
Large rebuild areas can make complex animations less efficient.
Mistake 5: Ignoring Curves
A linear animation may feel mechanical in situations where an easing curve would provide a more natural effect.
Mistake 6: Using Too Many Animations
Too much motion can reduce clarity and distract users.
53. Recommended Animation Development Process
- Identify what should change visually
- Decide whether the change needs animation
- Try an implicit animation first for simple property changes
- Use
TweenAnimationBuilder for custom implicit animation needs
- Use explicit animation when precise control is required
- Create an
AnimationController
- Choose a suitable duration and appropriate curve
- Use a
Tween when the output range differs from 0.0 to 1.0
- Choose a transition widget,
AnimatedBuilder, or AnimatedWidget
- Dispose controllers correctly
- Test the animation on different devices
54. Practical Decision Tree
Do you need an animation?
↓ Yes
Is it a simple property change?
↓ Yes
Use an implicit animation (e.g. AnimatedContainer)
↓ No
Do you need custom values or timing?
↓ Yes
Consider TweenAnimationBuilder
↓ No
Do you need full control?
↓ Yes
Use AnimationController → Tween / Curve → Transition / AnimatedBuilder
Is the animation between routes?
↓ Yes
Consider Hero or route transitions
55. Quick Comparison of Important Animation Classes
| Class | Purpose |
Animation | Represents a value that changes over time |
AnimationController | Controls explicit animation progress |
Tween | Maps animation progress to a value range |
CurvedAnimation | Applies a timing curve |
AnimatedBuilder | Builds UI based on an animation |
AnimatedWidget | Creates reusable widgets driven by animations |
AnimatedContainer | Implicitly animates container properties |
AnimatedOpacity | Implicitly animates opacity |
AnimatedSwitcher | Animates between different children |
TweenAnimationBuilder | Creates custom implicit animations |
Hero | Animates a shared element between routes |
56. Interview Questions
Q1. What is an animation in Flutter?
An animation is a visual change in a widget or interface that occurs over time rather than instantly.
Q2. What are implicit animations?
Implicit animations automatically animate changes between old and new property values. Flutter manages the intermediate values.
Q3. What is an explicit animation?
An explicit animation is one where the developer controls the animation lifecycle, commonly using AnimationController.
Q4. What is AnimationController?
AnimationController controls an explicit animation and generates values over time.
Q5. What is Tween?
A Tween defines a beginning and ending value and interpolates between them based on animation progress.
Q6. What is CurvedAnimation?
CurvedAnimation modifies the timing behavior of an animation by applying a curve.
Q7. What is AnimatedBuilder?
AnimatedBuilder listens to an animation and rebuilds the widget returned by its builder whenever the animation value changes.
Q8. What is Hero animation?
A Hero animation creates a shared-element transition between two routes using matching Hero tags.
Q9. Why should AnimationController be disposed?
It should be disposed when no longer needed to release the resources associated with the controller.
Q10. What is the difference between implicit and explicit animation?
Implicit animations are simpler and allow Flutter to manage the transition automatically, while explicit animations provide direct control over timing, progress, repetition, reversal, and other animation behavior.
57. Quick Revision
| Concept | Remember |
| Animation | Value that changes over time |
| Implicit Animation | Flutter manages the transition |
| Explicit Animation | Developer controls the animation |
AnimationController | Controls animation progress |
Tween | Defines beginning and ending values |
| Curve | Controls animation timing behavior |
AnimatedBuilder | Builds widgets from animation values |
AnimatedWidget | Reusable animation-driven widget |
| Hero | Shared element route animation |
| Staggered Animation | Multiple animations with different timing |
| Physics Animation | Simulation-based natural motion |
58. Learning Outcome
After completing this topic, you should be able to:
- Explain what animations are in Flutter
- Understand why animations are useful in UI development
- Differentiate implicit and explicit animations
- Use
AnimatedContainer, AnimatedOpacity, AnimatedPadding, AnimatedPositioned, and AnimatedSwitcher
- Use
TweenAnimationBuilder
- Understand
AnimationController and vsync
- Create and use
Tween and CurvedAnimation
- Use
AnimatedBuilder and AnimatedWidget
- Use
FadeTransition, ScaleTransition, SlideTransition, and RotationTransition
- Understand Hero and staggered animations
- Understand animation status and listeners
- Build reusable and maintainable animations
- Consider animation performance and accessibility
59. Summary
Flutter provides a powerful animation system that ranges from simple implicit animations to highly customizable explicit animations. Implicit widgets such as AnimatedContainer, AnimatedOpacity, and AnimatedSwitcher are useful when Flutter can automatically handle the transition between property values.
When more control is required, explicit animation APIs such as AnimationController, Tween, CurvedAnimation, AnimatedBuilder, and transition widgets provide detailed control over animation behavior. Flutter also supports advanced patterns such as Hero animations, staggered animations, animated lists, route transitions, and physics-based animations.
The best approach is to start with the simplest animation that meets the requirement and move to explicit or more advanced techniques when additional control is actually needed. Well-designed animations should improve clarity, feedback, and usability without making the application feel unnecessarily slow or distracting.